package yhao.demo.service.erp.workflow.service;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yhao.demo.service.erp.workflow.apilist.enums.flow.ProcessDefinitionStatus;
import yhao.demo.service.erp.workflow.apilist.form.flow.*;
import yhao.demo.service.erp.workflow.apilist.model.flow.FlowNodeModel;
import yhao.demo.service.erp.workflow.apilist.model.flow.FlowNodeTemplateModel;
import yhao.demo.service.erp.workflow.apilist.model.flow.FlowNodeWarnTemplateModel;
import yhao.demo.service.erp.workflow.apilist.model.flow.FlowProcessDefinitionModel;
import yhao.demo.service.erp.workflow.dao.ProcessDao;
import yhao.demo.service.erp.workflow.service.pvm.ProcessSearcher;
import yhao.infra.apilist.validate.IdForm;
import yhao.infra.common.model.Pagination;
import yhao.infra.common.util.CommonListUtil;

import javax.annotation.Resource;
import java.util.*;

@Service
public class ProcessService {
    @Resource
    private ProcessDao processDao;

    @Autowired
    private ProcessSearcher processSearcher;

    @Transactional
    public boolean updateBizCode(FlowProcessBizCodeChangeForm form){
        if(StringUtils.isEmpty(form.getBizCodeStr())&&form.getBizCode()==null){
            throw new RuntimeException("必须设置业务编码");
        }
        if(form.getBizCode()!=null){        //优先使用预定义的业务编码
            form.setBizCodeStr(form.getBizCode().toString());
        }
        FlowProcessDefinitionModel model = processDao.selectProcessDefinitionById(form.getId());
        if(model==null || model.getStatus()!= ProcessDefinitionStatus.ENABLED){
            throw new RuntimeException("流程定义不存在或已过期");
        }
        processDao.overrideProcessDefinitionByBizCode(form.getBizCodeStr());
        processDao.updateBizCode(form);
        processSearcher.removeProcessDefinitionCacheByCode(model.getBizCode());
        processSearcher.removeProcessDefinitionCacheByCode(form.getBizCodeStr());
        return true;
    }

    @Transactional
    public FlowProcessDefinitionForm saveProcessDefinition(FlowProcessDefinitionForm form) {
        if(StringUtils.isEmpty(form.getBizCodeStr())&&form.getBizCode()==null){
            throw new RuntimeException("必须设置业务编码");
        }
        if(form.getBizCode()!=null){        //优先使用预定义的业务编码
            form.setBizCodeStr(form.getBizCode().toString());
        }

        //校验有效性
        if(checkLoopProcessDefinition(form)){
           throw new RuntimeException("存在有循环引用，请重新设置");
        }
        String errMsg = checkAndSetTransProcessDefinition(form);
        if(StringUtils.isNotEmpty(errMsg)){
            throw new RuntimeException(errMsg);
        }
        if(StringUtils.isNotEmpty(form.getId())){
            //编辑流程，则原来的流程设置为过期。
            processDao.overrideProcessDefinitionById(form.getId());
            processSearcher.removeProcessDefinitionCacheById(form.getId());
        }
        processSearcher.removeProcessDefinitionCacheByCode(form.getBizCodeStr());
        //编辑流程，则原来的流程设置为过期。
        processDao.overrideProcessDefinitionByBizCode(form.getBizCodeStr());
        //将原来关联到
        String processId = UUID.randomUUID().toString();
        form.setId(processId);
        //流程
        processDao.insertProcessDefinition(form);
        //节点
        for(FlowNodeForm node:form.getNodes()){
            node.setProcessId(processId);
            //节点事项
            if(!CommonListUtil.isEmpty(node.getItemList())){
                for(FlowNodeItemForm item:node.getItemList()){
                    item.setNodeId(node.getId());
                    item.setId(UUID.randomUUID().toString());
                }
                processDao.batchInsertFlowNodeItem(node.getItemList());
            }
            //节点通知
            if(!CommonListUtil.isEmpty(node.getNotifyList())){
                for(FlowNodeNotifyForm notify:node.getNotifyList()){
                    notify.setNodeId(node.getId());
                    notify.setId(UUID.randomUUID().toString());
                }
                processDao.batchInsertFlowNodeNotify(node.getNotifyList());
            }
            //预警设置
            if(node.getWarn()!=null && node.getWarn() && node.getWarnForm()!=null){
                FlowNodeWarnForm warnForm = node.getWarnForm();
                warnForm.setNodeId(node.getId());
                warnForm.setId(UUID.randomUUID().toString());
                processDao.insertFlowNodeWarn(warnForm);
            }
            //校验是否有子任务，有则至多只能引用一级子任务
            if(StringUtils.isNotEmpty(node.getSubProcessId())){
                FlowProcessDefinitionModel subDef = processSearcher.searchProcessDefinitionById(node.getSubProcessId());
                for(FlowNodeModel model:subDef.getNodeList()){
                    if(StringUtils.isNotEmpty(model.getSubProcessId())){
                        throw new RuntimeException("节点包含的子流程不可再引用其他子流程，至多嵌套1层子流程！请重新设置");
                    }
                }
            }
        }
        processDao.batchInsertFlowNode(form.getNodes());
        //连接点
        for(FlowTransitionForm tran:form.getTrans()){
            tran.setProcessId(processId);
        }
        processDao.batchInsertFlowTransition(form.getTrans());
        return form;
    }

    private String checkAndSetTransProcessDefinition(FlowProcessDefinitionForm form) {
        Map<Integer, FlowNodeForm> nodeMap = new HashMap<>();
        for(FlowNodeForm node:form.getNodes()){
            node.setId(UUID.randomUUID().toString());
            if(!nodeMap.containsKey(node.getOrderNo())){
                nodeMap.put(node.getOrderNo(),node);
            }
        }
        if(nodeMap.values().size()!=form.getNodes().size()){
            return "节点编号重复，请重新设置";
        }
        Map<Integer,Integer> sourceCountMap = new HashMap<>();
        for(FlowTransitionForm tran:form.getTrans()){
            FlowNodeForm source = nodeMap.get(tran.getSourceOrderNo());
            FlowNodeForm destination = nodeMap.get(tran.getDestinationOrderNo());
            if(source==null || destination==null){
                return "连接未编号的节点，请重新设置";
            }
            tran.setId(UUID.randomUUID().toString());
            tran.setSourceId(source.getId());
            tran.setDestinationId(destination.getId());
            if(!sourceCountMap.containsKey(tran.getSourceOrderNo())){
                sourceCountMap.put(tran.getSourceOrderNo(),0);
            }
            if(!sourceCountMap.containsKey(tran.getDestinationOrderNo())){
                sourceCountMap.put(tran.getDestinationOrderNo(),0);
            }
            //给目的节点的来源count+1
            sourceCountMap.put(tran.getDestinationOrderNo(),sourceCountMap.get(tran.getDestinationOrderNo())+1);
        }
        if(sourceCountMap.keySet().size()!=form.getNodes().size()){
            return "存在孤立的节点，请重新设置";
        }
        //有且只有一个节点不存在来源节点，即初始节点，其他所有节点必须都有来源节点。
        int rootCount = 0;
        for(Integer count:sourceCountMap.values()){
            if(count==0){
                rootCount++;
            }
        }
        if(rootCount>1){
            return "起始节点数量大于1，请重新设置";
        }else if(rootCount == 0){
            return "没有起始节点，请重新设置";
        }
        return null;
    }

    private boolean checkLoopProcessDefinition(FlowProcessDefinitionForm form) {
        //检查是否成环
        Map<Integer, Set<Integer>> checkMap = new HashMap<>();
        for(FlowTransitionForm tran:form.getTrans()){
            if(!checkMap.containsKey(tran.getSourceOrderNo())){
                checkMap.put(tran.getSourceOrderNo(),new HashSet<>());
            }
            checkMap.get(tran.getSourceOrderNo()).add(tran.getDestinationOrderNo());
        }
        for(Integer source:checkMap.keySet()){
            for(Integer destination : checkMap.get(source)){
                if(checkMap.get(destination)!=null &&
                        checkMap.get(destination).contains(source)){
                    return true;
                }
            }
        }
        return false;
    }

    @Transactional
    public FlowNodeTemplateForm saveNodeTemplate(FlowNodeTemplateForm form) {
        boolean insert = false;
        if(StringUtils.isNotEmpty(form.getId())){
            processDao.updateNodeTemplate(form);
        }else{
            insert = true;
            form.setId(UUID.randomUUID().toString());
            processDao.saveNodeTemplate(form);
        }
        FlowNodeWarnForm warnForm = form.getWarnForm();
        if(form.getWarn()&&warnForm!=null){
            if(warnForm.getLimitWarn()>=warnForm.getLimit()){
                throw new RuntimeException("限定完成提醒时间不得大于限定时间");
            }
            if(warnForm.getLevelOneWarn()>=warnForm.getLevelTwoWarn()){
                throw new RuntimeException("1级预警时间不得大于2级预警时间");
            }
            if(warnForm.getLevelTwoWarn()>=warnForm.getLevelThreeWarn()){
                throw new RuntimeException("2级预警时间不得大于3级预警时间");
            }
            warnForm.setNodeId(form.getId());
            FlowNodeWarnTemplateModel warn =
                    insert?null:processDao.selectTemplateNodeWarnByTemplateNodeId(form.getId());
            if(warn==null){
                warnForm.setId(UUID.randomUUID().toString());
                processDao.insertFlowNodeWarnTemplate(warnForm);
            }else{
                processDao.updateFlowNodeWarnTemplate(warnForm);
            }
        }
        return form;
    }

    public FlowNodeItemTemplateForm saveNodeItemTemplate(FlowNodeItemTemplateForm form) {
        if(StringUtils.isNotEmpty(form.getId())){
            processDao.updateNodeItemTemplate(form);
        }else{
            form.setId(UUID.randomUUID().toString());
            processDao.saveNodeItemTemplate(form);
        }
        return form;
    }

    public List<FlowNodeTemplateModel> queryNodeTemplate() {
        return processDao.queryAllNodeTemplate();
    }

    public FlowNodeWarnTemplateModel queryNodeWarnTemplateByNodeTemplateId(IdForm form) {
        return processDao.selectTemplateNodeWarnByTemplateNodeId(form.getId());
    }

    public Pagination<FlowProcessDefinitionModel> queryProcessDefinition(FlowProcessQueryForm form) {
        Pagination<FlowProcessDefinitionModel> page = new Pagination<>(form.getPageSize(),form.getCurrentPage());
        processDao.queryProcessDefinition(page,form);
        return page;
    }
}
